IMDb Time Series Analysis
By Series
## get summary info for each season
series_info <- scoobydoo %>%
filter(
!(season %in% c("Movie", "Special"))
) %>%
mutate(
year = year(date_aired),
imdb = as.double(imdb),
engagement = as.double(engagement)
) %>%
group_by(series_name, network) %>%
dplyr::summarise(
series_start = min(date_aired),
series_end = max(date_aired),
n_episodes = n(),
mean_imdb = mean(imdb, na.rm = TRUE),
mean_engagement = mean(engagement, na.rm = TRUE)
) %>%
ungroup() %>%
filter(n_episodes > 1) %>% # filter out movie events
arrange(series_start, series_end) %>%
mutate(
series_id = as.double(row_number())
)
series_info <- series_info %>%
mutate(
ranking = case_when(
series_id == head(arrange(series_info, desc(mean_imdb)), 1)$series_id ~ "Best Series",
series_id %in% head(arrange(series_info, desc(mean_imdb)), 3)$series_id ~ "Top 3 Series",
series_id == head(arrange(series_info, mean_imdb), 1)$series_id ~ "Worst Series",
series_id %in% head(arrange(series_info, mean_imdb), 3)$series_id ~ "Bottom 3 Series",
TRUE ~ "Other"
),
ranking = factor(ranking, levels = c("Best Series", "Top 3 Series", "Other", "Bottom 3 Series", "Worst Series"))
)
# plot series over time
series_info %>%
plot_ly(
type = 'bar',
mode = 'markers',
x = ~series_id,
y = ~mean_imdb,
color = ~ranking,
colors = color_scheme1,
text = ~paste0("<b>", series_name, "</b><br>",
"<i>Aired from ", series_start, " to ", series_end, " on ", network, "</i><br><br>",
"Mean IMDb Score: ", round(mean_imdb, 2), " (Number of Reviews: ", round(mean_engagement), ")<br>",
"Episodes: ", n_episodes, "<br>")
) %>%
layout(
title = 'IMDb Scores for Scooby Doo Series Over Time',
xaxis = list(title = 'Sequential Series Number', showticklabels = FALSE),
yaxis = list(title = 'Mean IMDb Score'),
legend = list(orientation = 'h', y = -0.3),
width = 900,
height = 400
)
By Seasons
## get summary info for each season
season_info <- scoobydoo %>%
mutate(
year = year(date_aired),
imdb = as.double(imdb),
engagement = as.double(engagement)
) %>%
group_by(series_name, network, season) %>%
dplyr::summarise(
season_start = min(date_aired),
season_end = max(date_aired),
n_episodes = n(),
mean_imdb = mean(imdb, na.rm = TRUE),
mean_engagement = mean(engagement, na.rm = TRUE)
) %>%
ungroup() %>%
filter(n_episodes > 1 & !(season %in% c("Movie", "Special"))) %>% # filter out movie events
arrange(season_start, season_end) %>%
mutate(
season_id = as.double(row_number())
)
season_info <- season_info %>%
mutate(
ranking = case_when(
season_id == head(arrange(season_info, desc(mean_imdb)), 1)$season_id ~ "Best Season",
season_id %in% head(arrange(season_info, desc(mean_imdb)), 3)$season_id ~ "Top 3 Season",
season_id == head(arrange(season_info, mean_imdb), 1)$season_id ~ "Worst Season",
season_id %in% head(arrange(season_info, mean_imdb), 3)$season_id ~ "Bottom 3 Season",
TRUE ~ "Other"
),
ranking = factor(ranking, levels = c("Best Season", "Top 3 Season", "Other", "Bottom 3 Season", "Worst Season"))
)
The average Scooby Doo TV series has 2.0625 seasons. Thatās so few!
# plot season over time
season_info %>%
plot_ly(
type = 'bar',
mode = 'markers',
x = ~season_id,
y = ~mean_imdb,
color = ~ranking,
colors = color_scheme1,
text = ~paste0("<b>", series_name, " - Season ", season, "</b><br>",
"<i>Aired from ", season_start, " to ", season_end, " on ", network, "</i><br><br>",
"Mean IMDb Score: ", round(mean_imdb, 2), " (Number of Reviews: ", round(mean_engagement), ")<br>",
"Episodes: ", n_episodes, "<br>")
) %>%
layout(
title = 'IMDb Scores for Scooby Doo Seasons Over Time',
xaxis = list(title = 'Sequential Season Number', showticklabels = FALSE),
yaxis = list(title = 'Mean IMDb Score'),
legend = list(orientation = 'h', y = -0.3),
width = 900,
height = 400
)
By Episodes
scoobydoo %>%
left_join(series_info, by = c("series_name", "network")) %>%
filter(
!(is.na(imdb)),
imdb != "NULL",
engagement != "NULL",
!(season %in% c("Movie", "Special"))
) %>%
plot_ly(
type = 'scatter',
mode = 'markers',
x = ~index,
y = ~imdb,
color = ~ranking,
colors = color_scheme1,
text = ~paste0("<b>", title, "</b><br>",
"<i>Season ", season, " of Series ", series_name, "</i><br><br>",
"Aired ", date_aired, " on ", network, "<br>",
"IMDb Score ", imdb, " (Number of Reviews: ", engagement, ")")
) %>%
layout(
title = 'IMDb scores of Scooby Doo episodes over time',
xaxis = list(title = 'Episode Index (according to Scoobypedia)'),
yaxis = list(title = 'IMDb Score'),
width = 900,
height = 400,
legend = list(orientation = 'h', y = -0.3)
)
Monsters
# collapse columns
monsters <- scoobydoo %>%
mutate(
# caught
caught_fred = str_replace(caught_fred, "TRUE", "Fred"),
caught_daphnie = str_replace(caught_daphnie, "TRUE", "Daphnie"),
caught_velma = str_replace(caught_velma, "TRUE", "Velma"),
caught_shaggy = str_replace(caught_shaggy, "TRUE", "Shaggy"),
caught_scooby = str_replace(caught_scooby, "TRUE", "Scooby"),
# captured
captured_fred = str_replace(captured_fred, "TRUE", "Fred"),
captured_daphnie = str_replace(captured_daphnie, "TRUE", "Daphnie"),
captured_velma = str_replace(captured_velma, "TRUE", "Velma"),
captured_shaggy = str_replace(captured_shaggy, "TRUE", "Shaggy"),
captured_scooby = str_replace(captured_scooby, "TRUE", "Scooby"),
# unmasked
unmask_fred = str_replace(unmask_fred, "TRUE", "Fred"),
unmask_daphnie = str_replace(unmask_daphnie, "TRUE", "Daphnie"),
unmask_velma = str_replace(unmask_velma, "TRUE", "Velma"),
unmask_shaggy = str_replace(unmask_shaggy, "TRUE", "Shaggy"),
unmask_scooby = str_replace(unmask_scooby, "TRUE", "Scooby"),
)
Who caught the monsters?
# tidy data frame for monsters caught
caught_df <- monsters
caught_df$caught_by <- apply(caught_df %>% select(starts_with("caught_")), 1, function(x) paste(x[x != "FALSE" & x != "NULL"], collapse = ","))
caught_df$caught_by[is.na(caught_df$caught_by)] <- "Not Caught"
caught_df <- caught_df %>%
separate_rows(caught_by, sep = ",") %>%
filter(
caught_by %in% the_gang
)
# table of most catches
count(caught_df, caught_by) %>%
arrange(desc(n))
Who was captured by monsters the most?
# tidy data frame for captured by the monster
captured_df <- monsters
captured_df$captured <- apply(captured_df %>% select(starts_with("captured_")), 1, function(x) paste(x[x != "FALSE" & x != "NULL"], collapse = ","))
#captured$captured_by[is.na(captured$caught_by)] <- "Not Caught"
captured_df <- captured_df %>%
separate_rows(captured, sep = ",") %>%
filter(
captured %in% the_gang
)
count(captured_df, captured) %>%
arrange(desc(n))
# plot the relationship
captured_df2 <- captured_df %>%
mutate(
year = year(date_aired),
real = ifelse(monster_real == "TRUE", "Real", "Fake"),
monster_details = paste0(monster_name, " (", real, " Monster(s)) in '", title, "' on ", date_aired, ".")
) %>%
group_by(year, captured) %>%
dplyr::summarise(
n = n(),
captured_details = paste(monster_details, collapse = "\n")
)
plot_monsters_captures <- function(gang_member, df = captured_df2) {
fig <- df %>%
filter(captured == gang_member) %>%
plot_ly(
type = 'bar',
x = ~year,
y = ~n,
text = ~paste0("<b>Captured By...</b><br><br>",
captured_details),
showlegend = FALSE
) %>%
layout(
xaxis = list(
showline = TRUE,
showticklabels = FALSE,
mirror = "ticks",
linecolor = toRGB("black"),
linewidth = 2,
range = c(min(captured_df2$year), max(captured_df2$year))
),
yaxis = list(
title = ~paste0("<b>", gang_member, "</b>"),
showline = TRUE,
mirror = "ticks",
linecolor = toRGB("black"),
linewidth = 2,
range = c(min(captured_df2$n), max(captured_df2$n) + 1)
)
)
fig
}
subplot(
plot_monsters_captures("Fred"),
plot_monsters_captures("Daphnie"),
plot_monsters_captures("Velma"),
plot_monsters_captures("Shaggy"),
plot_monsters_captures("Scooby"),
nrows = length(the_gang),
shareX = FALSE,
shareY = FALSE,
titleX = FALSE,
titleY = TRUE
) %>%
layout(
mode = 'lines+markers',
title = '<b><i>Jinkies! ... has been captured by the monster!</i></b>',
width = 900,
height = 600,
legend = list(orientation = 'h')
)
Who unmasked the most monsters?
# tidy data frame for captured by the monster
unmasked_df <- monsters
unmasked_df$unmasked_by <- apply(unmasked_df %>% select(starts_with("unmask_")), 1, function(x) paste(x[x != "FALSE" & x != "NULL"], collapse = ","))
unmasked_df$unmasked_by[is.na(unmasked_df$unmasked_by)] <- "Not Unmasked"
unmasked_df <- unmasked_df %>%
separate_rows(unmasked_by, sep = ",") %>%
filter(
unmasked_by %in% the_gang
)
count(unmasked_df, unmasked_by) %>%
arrange(desc(n))
# plot the relationship
unmasked_df2 <- unmasked_df %>%
mutate(
year = year(date_aired),
real = ifelse(monster_real == "TRUE", "Real", "Fake"),
monster_details = paste0(monster_name, " (", real, " Monster(s)) in '", title, "' on ", date_aired, ".")
) %>%
group_by(year, unmasked_by) %>%
dplyr::summarise(
n = n(),
unmask_details = paste(monster_details, collapse = "\n")
)
plot_monsters_unmasks <- function(gang_member, df = unmasked_df2) {
fig <- df %>%
filter(unmasked_by == gang_member) %>%
plot_ly(
type = 'bar',
x = ~year,
y = ~n,
text = ~paste0("<b>Unmasked By...</b><br><br>",
unmask_details),
showlegend = FALSE
) %>%
layout(
xaxis = list(
showline = TRUE,
showticklabels = FALSE,
mirror = "ticks",
linecolor = toRGB("black"),
linewidth = 2,
range = c(min(unmasked_df2$year), max(unmasked_df2$year))
),
yaxis = list(
title = ~paste0("<b>", gang_member, "</b>"),
showline = TRUE,
mirror = "ticks",
linecolor = toRGB("black"),
linewidth = 2,
range = c(min(unmasked_df2$n), max(unmasked_df2$n) + 1)
)
)
fig
}
subplot(
plot_monsters_unmasks("Fred"),
plot_monsters_unmasks("Daphnie"),
plot_monsters_unmasks("Velma"),
plot_monsters_unmasks("Shaggy"),
plot_monsters_unmasks("Scooby"),
nrows = length(the_gang),
shareX = FALSE,
shareY = FALSE,
titleX = FALSE,
titleY = TRUE
) %>%
layout(
mode = 'lines+markers',
title = '<b><i>Monsters Unmasked Annualy By...</i></b>',
width = 900,
height = 600,
legend = list(orientation = 'h')
)
What monster types are most likely to get away?
n_not_caught <- nrow(filter(scoobydoo, caught_not == "TRUE"))
got_away_rate <- paste0(round(100 * (n_not_caught / nrow(scoobydoo)), 2), "%")
First of all, the monster(s) got away at the end of only 31 out of 603 episodes (rate of 5.14%). Of those 31, the following breaks down the success rate by monster type.
escape_stats <- scoobydoo %>%
filter(caught_not == "TRUE") %>%
# looks like there were a few mispellings
mutate(monster_type = ifelse(str_detect(monster_type, "(Disguised|Disguised|Disugised)"), "Disguised", monster_type)) %>%
separate_rows(monster_type, sep = ",") %>%
group_by(monster_type) %>%
dplyr::summarise(
n_escaped = n(),
mean_imdb_escaped = mean(as.double(imdb))
)
imdb_effect_categories <- c(
"Better Than Average Episode",
"Slightly Better Than Average Episode",
"Slightly Worse Than Average Episode",
"Worse Than Average Episode"
)
escape_stats2 <- scoobydoo %>%
mutate(monster_type = ifelse(str_detect(monster_type, "(Disguised|Disguised|Disugised)"), "Disguised", monster_type)) %>%
separate_rows(monster_type, sep = ",") %>%
filter(imdb != "NULL") %>%
group_by(monster_type) %>%
dplyr::summarise(
n_total = n(),
mean_imdb = mean(as.double(imdb))
) %>%
inner_join(escape_stats) %>%
mutate(
escape_rate = round(100 * (n_escaped / n_total)),
escape_imdb_effect = mean_imdb_escaped - mean_imdb,
imdb_effect_category = case_when(
escape_imdb_effect < -1 ~ "Worse Than Average Episode",
escape_imdb_effect < 0 ~ "Slightly Worse Than Average Episode",
escape_imdb_effect < 1 ~ "Slightly Better Than Average Episode",
escape_imdb_effect >= 1 ~ "Better Than Average Episode"
),
imdb_effect_category = factor(imdb_effect_category, levels = imdb_effect_categories)
)
escape_stats2 %>%
plot_ly(
type = 'bar',
x = ~fct_reorder(monster_type, escape_rate, .desc = TRUE),
y = ~escape_rate,
color = ~imdb_effect_category,
colors = color_scheme2,
text = ~paste0("<b>", monster_type, "</b><br><br>",
"Escaped ", n_escaped, " out of ", n_total, " episodes.<br>",
"Average Episode IMDb Score (All): ", round(mean_imdb, 1), "<br>",
"Average Episode IMDb Score (Escaped): ", round(mean_imdb_escaped, 1))
) %>%
layout(
title = 'How often do Scooby Doo monsters go uncaught, and how does that affect the episode?',
xaxis = list(title = "Monster Type"),
yaxis = list(title = "Escape Rate (%)"),
legend = list(orientation = 'h', y = -0.3),
width = 900,
height = 500
)
LS0tDQp0aXRsZTogIjIwMjEwNzEzIC0gU2Nvb2J5IERvbyINCmF1dGhvcjogIk5pY2sgQ3J1aWNrc2hhbmsiDQpkYXRlOiAiNy8xNC8yMDIxIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSkNCmBgYA0KDQpgYGB7ciBsaWJyYXJpZXN9DQojIGxpYnJhcmllcw0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpgYGB7cn0NCiMgZGF0YQ0Kc2Nvb2J5ZG9vIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDctMTMvc2Nvb2J5ZG9vLmNzdicpDQpgYGANCg0KIVtzY29vYnkgZG9vXShodHRwczovL21lZGlhLmF2YWxvbmhpbGwud2l6YXJkcy5jb20vc3R5bGVzL3NlY29uZF9odWJwYWdlX2Jhbm5lci9wdWJsaWMvaW1hZ2VzL2R5bmFtaWNodWJwYWdlL2x3Y2xrd2N3cWNuLmpwZykNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyB3ZWVrcyBbVGlkeSBUdWVzZGF5XShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L3RyZWUvbWFzdGVyL2RhdGEvMjAyMS8yMDIxLTA3LTEzKSBkYXRhc2V0IGNvbWVzIGZyb20gW0thZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS93aWxsaWFtc2Nob29sZW1hbi9zY29vYnlkb28tY29tcGxldGUpIGJ5IHdheSBvZiBtYW51YWwgZGF0YSBhZ2dyZWdhdGlvbiBmcm9tIFtwbHVtbXllXShodHRwczovL3d3dy5rYWdnbGUuY29tL3dpbGxpYW1zY2hvb2xlbWFuKS4NCg0KPiBFdmVyeSBTY29vYnktRG9vIGVwaXNvZGUgYW5kIG1vdmllJ3MgdmFyaW91cyB2YXJpYWJsZXMuDQo+DQo+IFRvb2sgfjEgeWVhciB0byB3YXRjaCBldmVyeSBTY29vYnktRG9vIGl0ZXJhdGlvbiBhbmQgdHJhY2sgZXZlcnkgdmFyaWFibGUuIE1hbnkgdmFsdWVzIGFyZSBzdWJqZWN0aXZlIGJ5IG5hdHVyZSBvZiB3YXRjaGluZyBidXQgSSB0cmllZCBteSBoYXJkZXN0IHRvIGtlZXAgdGhlIGRhdGEgY29sbGVjdGlvbiBjb25zaXN0ZW50Lg0KPg0KPiBJZiB5b3UgcGxhbiB0byB1c2UgdGhpcyBkYXRhIGZvciBhbnl0aGluZyBzY2hvb2wvZW50ZXJ0YWlubWVudCByZWxhdGVkIHlvdSBhcmUgZnJlZSB0byAoY3JlZGl0IGlzIGFsd2F5cyB3ZWxjb21lKS4NCg0KTm90ZWJvb2sgY2FuIGJlIHZpZXdlZCBieSBwYXN0aW5nIHRoZSBnaXRodWIgVVJMIHRvIHRoaXMgZG9jdW1lbnQgaW50byBodHRwczovL25idmlld2VyLmp1cHl0ZXIub3JnLy4NCg0KYGBge3J9DQpzY29vYnlkb28gJT4lIGhlYWQoNSkNCmBgYA0KDQpgYGB7cn0NCiMgdmFsdWVzDQpjb2xvcl9zY2hlbWUxIDwtIGMoDQogICIjMjI4QjIyIiwgIyBha2EgZm9yZXN0IGdyZWVuIChCZXN0KQ0KICAiIzk4RkI5OCIsICMgYWthIHBhbGUgZ3JlZW4gKFRvcCAzKQ0KICAiI0QzRDNEMyIsICMgYWthIGxpZ2h0IGdyZXkgKE90aGVyKQ0KICAiI0ZGQjZDMSIsICMgYWthIGxpZ2h0IHBpbmsgKEJvdHRvbSAzKQ0KICAiI0RDMTQzQyIgIyBha2EgY3JpbXNvbiAoV29yc3QpDQogICkNCg0KY29sb3Jfc2NoZW1lMiA8LSBjKA0KICAiIzIyOEIyMiIsICMgYWthIGZvcmVzdCBncmVlbiAoR3JlYXQpDQogICIjOThGQjk4IiwgIyBha2EgcGFsZSBncmVlbiAoT0spDQogICIjRkZCNkMxIiwgIyBha2EgbGlnaHQgcGluayAoTWVoKQ0KICAiI0RDMTQzQyIgIyBha2EgY3JpbXNvbiAoV29yc3QpDQogICkNCg0KdGhlX2dhbmcgPC0gYygiRnJlZCIsICJEYXBobmllIiwgIlZlbG1hIiwgIlNoYWdneSIsICJTY29vYnkiKQ0KYGBgDQoNCiMgSU1EYiBUaW1lIFNlcmllcyBBbmFseXNpcw0KDQojIyBCeSBTZXJpZXMNCg0KYGBge3J9DQojIyBnZXQgc3VtbWFyeSBpbmZvIGZvciBlYWNoIHNlYXNvbg0Kc2VyaWVzX2luZm8gPC0gc2Nvb2J5ZG9vICU+JQ0KICBmaWx0ZXIoDQogICAgIShzZWFzb24gJWluJSBjKCJNb3ZpZSIsICJTcGVjaWFsIikpDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICBpbWRiID0gYXMuZG91YmxlKGltZGIpLA0KICAgIGVuZ2FnZW1lbnQgPSBhcy5kb3VibGUoZW5nYWdlbWVudCkNCiAgKSAlPiUNCiAgZ3JvdXBfYnkoc2VyaWVzX25hbWUsIG5ldHdvcmspICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIHNlcmllc19zdGFydCA9IG1pbihkYXRlX2FpcmVkKSwNCiAgICBzZXJpZXNfZW5kID0gbWF4KGRhdGVfYWlyZWQpLA0KICAgIG5fZXBpc29kZXMgPSBuKCksDQogICAgbWVhbl9pbWRiID0gbWVhbihpbWRiLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fZW5nYWdlbWVudCA9IG1lYW4oZW5nYWdlbWVudCwgbmEucm0gPSBUUlVFKQ0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGZpbHRlcihuX2VwaXNvZGVzID4gMSkgJT4lICMgZmlsdGVyIG91dCBtb3ZpZSBldmVudHMNCiAgYXJyYW5nZShzZXJpZXNfc3RhcnQsIHNlcmllc19lbmQpICU+JQ0KICBtdXRhdGUoDQogICAgc2VyaWVzX2lkID0gYXMuZG91YmxlKHJvd19udW1iZXIoKSkNCiAgKQ0KDQpzZXJpZXNfaW5mbyA8LSBzZXJpZXNfaW5mbyAlPiUNCiAgbXV0YXRlKA0KICAgIHJhbmtpbmcgPSBjYXNlX3doZW4oDQogICAgICBzZXJpZXNfaWQgPT0gaGVhZChhcnJhbmdlKHNlcmllc19pbmZvLCBkZXNjKG1lYW5faW1kYikpLCAxKSRzZXJpZXNfaWQgfiAiQmVzdCBTZXJpZXMiLA0KICAgICAgc2VyaWVzX2lkICVpbiUgaGVhZChhcnJhbmdlKHNlcmllc19pbmZvLCBkZXNjKG1lYW5faW1kYikpLCAzKSRzZXJpZXNfaWQgfiAiVG9wIDMgU2VyaWVzIiwNCiAgICAgIHNlcmllc19pZCA9PSBoZWFkKGFycmFuZ2Uoc2VyaWVzX2luZm8sIG1lYW5faW1kYiksIDEpJHNlcmllc19pZCB+ICJXb3JzdCBTZXJpZXMiLA0KICAgICAgc2VyaWVzX2lkICVpbiUgaGVhZChhcnJhbmdlKHNlcmllc19pbmZvLCBtZWFuX2ltZGIpLCAzKSRzZXJpZXNfaWQgfiAiQm90dG9tIDMgU2VyaWVzIiwNCiAgICAgIFRSVUUgfiAiT3RoZXIiDQogICAgKSwNCiAgICByYW5raW5nID0gZmFjdG9yKHJhbmtpbmcsIGxldmVscyA9IGMoIkJlc3QgU2VyaWVzIiwgIlRvcCAzIFNlcmllcyIsICJPdGhlciIsICJCb3R0b20gMyBTZXJpZXMiLCAiV29yc3QgU2VyaWVzIikpDQogICkNCmBgYA0KDQpgYGB7ciBzY29vYnkgc2VyaWVzIGltZGIgdGltZWxpbmV9DQojIHBsb3Qgc2VyaWVzIG92ZXIgdGltZQ0Kc2VyaWVzX2luZm8gJT4lDQogIHBsb3RfbHkoDQogICAgdHlwZSA9ICdiYXInLA0KICAgIG1vZGUgPSAnbWFya2VycycsDQogICAgeCA9IH5zZXJpZXNfaWQsDQogICAgeSA9IH5tZWFuX2ltZGIsDQogICAgY29sb3IgPSB+cmFua2luZywNCiAgICBjb2xvcnMgPSBjb2xvcl9zY2hlbWUxLA0KICAgIHRleHQgPSB+cGFzdGUwKCI8Yj4iLCBzZXJpZXNfbmFtZSwgIjwvYj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiPGk+QWlyZWQgZnJvbSAiLCBzZXJpZXNfc3RhcnQsICIgdG8gIiwgc2VyaWVzX2VuZCwgIiBvbiAiLCBuZXR3b3JrLCAiPC9pPjxicj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiTWVhbiBJTURiIFNjb3JlOiAiLCByb3VuZChtZWFuX2ltZGIsIDIpLCAiIChOdW1iZXIgb2YgUmV2aWV3czogIiwgcm91bmQobWVhbl9lbmdhZ2VtZW50KSwgIik8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiRXBpc29kZXM6ICIsIG5fZXBpc29kZXMsICI8YnI+IikNCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gJ0lNRGIgU2NvcmVzIGZvciBTY29vYnkgRG9vIFNlcmllcyBPdmVyIFRpbWUnLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdTZXF1ZW50aWFsIFNlcmllcyBOdW1iZXInLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnTWVhbiBJTURiIFNjb3JlJyksDQogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICdoJywgeSA9IC0wLjMpLA0KICAgIHdpZHRoID0gOTAwLA0KICAgIGhlaWdodCA9IDQwMA0KICApDQpgYGANCg0KIyMgQnkgU2Vhc29ucw0KDQpgYGB7cn0NCiMjIGdldCBzdW1tYXJ5IGluZm8gZm9yIGVhY2ggc2Vhc29uDQpzZWFzb25faW5mbyA8LSBzY29vYnlkb28gJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICBpbWRiID0gYXMuZG91YmxlKGltZGIpLA0KICAgIGVuZ2FnZW1lbnQgPSBhcy5kb3VibGUoZW5nYWdlbWVudCkNCiAgKSAlPiUNCiAgZ3JvdXBfYnkoc2VyaWVzX25hbWUsIG5ldHdvcmssIHNlYXNvbikgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgc2Vhc29uX3N0YXJ0ID0gbWluKGRhdGVfYWlyZWQpLA0KICAgIHNlYXNvbl9lbmQgPSBtYXgoZGF0ZV9haXJlZCksDQogICAgbl9lcGlzb2RlcyA9IG4oKSwNCiAgICBtZWFuX2ltZGIgPSBtZWFuKGltZGIsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9lbmdhZ2VtZW50ID0gbWVhbihlbmdhZ2VtZW50LCBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZmlsdGVyKG5fZXBpc29kZXMgPiAxICYgIShzZWFzb24gJWluJSBjKCJNb3ZpZSIsICJTcGVjaWFsIikpKSAlPiUgIyBmaWx0ZXIgb3V0IG1vdmllIGV2ZW50cw0KICBhcnJhbmdlKHNlYXNvbl9zdGFydCwgc2Vhc29uX2VuZCkgJT4lDQogIG11dGF0ZSgNCiAgICBzZWFzb25faWQgPSBhcy5kb3VibGUocm93X251bWJlcigpKQ0KICApDQoNCnNlYXNvbl9pbmZvIDwtIHNlYXNvbl9pbmZvICU+JQ0KICBtdXRhdGUoDQogICAgcmFua2luZyA9IGNhc2Vfd2hlbigNCiAgICAgIHNlYXNvbl9pZCA9PSBoZWFkKGFycmFuZ2Uoc2Vhc29uX2luZm8sIGRlc2MobWVhbl9pbWRiKSksIDEpJHNlYXNvbl9pZCB+ICJCZXN0IFNlYXNvbiIsDQogICAgICBzZWFzb25faWQgJWluJSBoZWFkKGFycmFuZ2Uoc2Vhc29uX2luZm8sIGRlc2MobWVhbl9pbWRiKSksIDMpJHNlYXNvbl9pZCB+ICJUb3AgMyBTZWFzb24iLA0KICAgICAgc2Vhc29uX2lkID09IGhlYWQoYXJyYW5nZShzZWFzb25faW5mbywgbWVhbl9pbWRiKSwgMSkkc2Vhc29uX2lkIH4gIldvcnN0IFNlYXNvbiIsDQogICAgICBzZWFzb25faWQgJWluJSBoZWFkKGFycmFuZ2Uoc2Vhc29uX2luZm8sIG1lYW5faW1kYiksIDMpJHNlYXNvbl9pZCB+ICJCb3R0b20gMyBTZWFzb24iLA0KICAgICAgVFJVRSB+ICJPdGhlciINCiAgICApLA0KICAgIHJhbmtpbmcgPSBmYWN0b3IocmFua2luZywgbGV2ZWxzID0gYygiQmVzdCBTZWFzb24iLCAiVG9wIDMgU2Vhc29uIiwgIk90aGVyIiwgIkJvdHRvbSAzIFNlYXNvbiIsICJXb3JzdCBTZWFzb24iKSkNCiAgKQ0KYGBgDQoNClRoZSBhdmVyYWdlIFNjb29ieSBEb28gVFYgc2VyaWVzIGhhcyBgciBtZWFuKGNvdW50KHNlYXNvbl9pbmZvLCBzZXJpZXNfbmFtZSkkbilgIHNlYXNvbnMuIFRoYXQncyBzbyBmZXchDQoNCmBgYHtyIHNjb29ieSBzZWFzb24gaW1kYiB0aW1lbGluZX0NCiMgcGxvdCBzZWFzb24gb3ZlciB0aW1lDQpzZWFzb25faW5mbyAlPiUNCiAgcGxvdF9seSgNCiAgICB0eXBlID0gJ2JhcicsDQogICAgbW9kZSA9ICdtYXJrZXJzJywNCiAgICB4ID0gfnNlYXNvbl9pZCwNCiAgICB5ID0gfm1lYW5faW1kYiwNCiAgICBjb2xvciA9IH5yYW5raW5nLA0KICAgIGNvbG9ycyA9IGNvbG9yX3NjaGVtZTEsDQogICAgdGV4dCA9IH5wYXN0ZTAoIjxiPiIsIHNlcmllc19uYW1lLCAiIC0gU2Vhc29uICIsIHNlYXNvbiwgIjwvYj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiPGk+QWlyZWQgZnJvbSAiLCBzZWFzb25fc3RhcnQsICIgdG8gIiwgc2Vhc29uX2VuZCwgIiBvbiAiLCBuZXR3b3JrLCAiPC9pPjxicj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiTWVhbiBJTURiIFNjb3JlOiAiLCByb3VuZChtZWFuX2ltZGIsIDIpLCAiIChOdW1iZXIgb2YgUmV2aWV3czogIiwgcm91bmQobWVhbl9lbmdhZ2VtZW50KSwgIik8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiRXBpc29kZXM6ICIsIG5fZXBpc29kZXMsICI8YnI+IikNCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gJ0lNRGIgU2NvcmVzIGZvciBTY29vYnkgRG9vIFNlYXNvbnMgT3ZlciBUaW1lJywNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnU2VxdWVudGlhbCBTZWFzb24gTnVtYmVyJywgc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ01lYW4gSU1EYiBTY29yZScpLA0KICAgIGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAnaCcsIHkgPSAtMC4zKSwNCiAgICB3aWR0aCA9IDkwMCwNCiAgICBoZWlnaHQgPSA0MDANCiAgKQ0KYGBgDQoNCg0KIyMgQnkgRXBpc29kZXMNCg0KYGBge3Igc2Nvb2J5IGltZGIgdGltZWxpbmUsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQ0Kc2Nvb2J5ZG9vICU+JQ0KICBsZWZ0X2pvaW4oc2VyaWVzX2luZm8sIGJ5ID0gYygic2VyaWVzX25hbWUiLCAibmV0d29yayIpKSAlPiUNCiAgICBmaWx0ZXIoDQogICAgIShpcy5uYShpbWRiKSksDQogICAgaW1kYiAhPSAiTlVMTCIsDQogICAgZW5nYWdlbWVudCAhPSAiTlVMTCIsDQogICAgIShzZWFzb24gJWluJSBjKCJNb3ZpZSIsICJTcGVjaWFsIikpDQogICkgJT4lDQogIHBsb3RfbHkoDQogICAgdHlwZSA9ICdzY2F0dGVyJywNCiAgICBtb2RlID0gJ21hcmtlcnMnLA0KICAgIHggPSB+aW5kZXgsDQogICAgeSA9IH5pbWRiLA0KICAgIGNvbG9yID0gfnJhbmtpbmcsDQogICAgY29sb3JzID0gY29sb3Jfc2NoZW1lMSwNCiAgICB0ZXh0ID0gfnBhc3RlMCgiPGI+IiwgdGl0bGUsICI8L2I+PGJyPiIsDQogICAgICAgICAgICAgICAgICAiPGk+U2Vhc29uICIsIHNlYXNvbiwgIiBvZiBTZXJpZXMgIiwgc2VyaWVzX25hbWUsICI8L2k+PGJyPjxicj4iLA0KICAgICAgICAgICAgICAgICAgIkFpcmVkICIsIGRhdGVfYWlyZWQsICIgb24gIiwgbmV0d29yaywgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgIklNRGIgU2NvcmUgIiwgaW1kYiwgIiAoTnVtYmVyIG9mIFJldmlld3M6ICIsIGVuZ2FnZW1lbnQsICIpIikNCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gJ0lNRGIgc2NvcmVzIG9mIFNjb29ieSBEb28gZXBpc29kZXMgb3ZlciB0aW1lJywNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnRXBpc29kZSBJbmRleCAoYWNjb3JkaW5nIHRvIFNjb29ieXBlZGlhKScpLA0KICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdJTURiIFNjb3JlJyksDQogICAgd2lkdGggPSA5MDAsDQogICAgaGVpZ2h0ID0gNDAwLA0KICAgIGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAnaCcsIHkgPSAtMC4zKQ0KICApDQpgYGANCg0KIyBNb25zdGVycyANCg0KYGBge3J9DQojIGNvbGxhcHNlIGNvbHVtbnMNCm1vbnN0ZXJzIDwtIHNjb29ieWRvbyAlPiUNCiAgbXV0YXRlKA0KICAgICMgY2F1Z2h0DQogICAgY2F1Z2h0X2ZyZWQgPSBzdHJfcmVwbGFjZShjYXVnaHRfZnJlZCwgIlRSVUUiLCAiRnJlZCIpLA0KICAgIGNhdWdodF9kYXBobmllID0gc3RyX3JlcGxhY2UoY2F1Z2h0X2RhcGhuaWUsICJUUlVFIiwgIkRhcGhuaWUiKSwNCiAgICBjYXVnaHRfdmVsbWEgPSBzdHJfcmVwbGFjZShjYXVnaHRfdmVsbWEsICJUUlVFIiwgIlZlbG1hIiksDQogICAgY2F1Z2h0X3NoYWdneSA9IHN0cl9yZXBsYWNlKGNhdWdodF9zaGFnZ3ksICJUUlVFIiwgIlNoYWdneSIpLA0KICAgIGNhdWdodF9zY29vYnkgPSBzdHJfcmVwbGFjZShjYXVnaHRfc2Nvb2J5LCAiVFJVRSIsICJTY29vYnkiKSwNCiAgICANCiAgICAjIGNhcHR1cmVkDQogICAgY2FwdHVyZWRfZnJlZCA9IHN0cl9yZXBsYWNlKGNhcHR1cmVkX2ZyZWQsICJUUlVFIiwgIkZyZWQiKSwNCiAgICBjYXB0dXJlZF9kYXBobmllID0gc3RyX3JlcGxhY2UoY2FwdHVyZWRfZGFwaG5pZSwgIlRSVUUiLCAiRGFwaG5pZSIpLA0KICAgIGNhcHR1cmVkX3ZlbG1hID0gc3RyX3JlcGxhY2UoY2FwdHVyZWRfdmVsbWEsICJUUlVFIiwgIlZlbG1hIiksDQogICAgY2FwdHVyZWRfc2hhZ2d5ID0gc3RyX3JlcGxhY2UoY2FwdHVyZWRfc2hhZ2d5LCAiVFJVRSIsICJTaGFnZ3kiKSwNCiAgICBjYXB0dXJlZF9zY29vYnkgPSBzdHJfcmVwbGFjZShjYXB0dXJlZF9zY29vYnksICJUUlVFIiwgIlNjb29ieSIpLA0KICAgIA0KICAgICMgdW5tYXNrZWQNCiAgICB1bm1hc2tfZnJlZCA9IHN0cl9yZXBsYWNlKHVubWFza19mcmVkLCAiVFJVRSIsICJGcmVkIiksDQogICAgdW5tYXNrX2RhcGhuaWUgPSBzdHJfcmVwbGFjZSh1bm1hc2tfZGFwaG5pZSwgIlRSVUUiLCAiRGFwaG5pZSIpLA0KICAgIHVubWFza192ZWxtYSA9IHN0cl9yZXBsYWNlKHVubWFza192ZWxtYSwgIlRSVUUiLCAiVmVsbWEiKSwNCiAgICB1bm1hc2tfc2hhZ2d5ID0gc3RyX3JlcGxhY2UodW5tYXNrX3NoYWdneSwgIlRSVUUiLCAiU2hhZ2d5IiksDQogICAgdW5tYXNrX3Njb29ieSA9IHN0cl9yZXBsYWNlKHVubWFza19zY29vYnksICJUUlVFIiwgIlNjb29ieSIpLA0KICApDQpgYGANCg0KIyMgV2hvIGNhdWdodCB0aGUgbW9uc3RlcnM/DQoNCmBgYHtyfQ0KIyB0aWR5IGRhdGEgZnJhbWUgZm9yIG1vbnN0ZXJzIGNhdWdodA0KY2F1Z2h0X2RmIDwtIG1vbnN0ZXJzDQpjYXVnaHRfZGYkY2F1Z2h0X2J5IDwtIGFwcGx5KGNhdWdodF9kZiAlPiUgc2VsZWN0KHN0YXJ0c193aXRoKCJjYXVnaHRfIikpLCAxLCBmdW5jdGlvbih4KSBwYXN0ZSh4W3ggIT0gIkZBTFNFIiAmIHggIT0gIk5VTEwiXSwgY29sbGFwc2UgPSAiLCIpKQ0KY2F1Z2h0X2RmJGNhdWdodF9ieVtpcy5uYShjYXVnaHRfZGYkY2F1Z2h0X2J5KV0gPC0gIk5vdCBDYXVnaHQiDQpjYXVnaHRfZGYgPC0gY2F1Z2h0X2RmICU+JQ0KICBzZXBhcmF0ZV9yb3dzKGNhdWdodF9ieSwgc2VwID0gIiwiKSAlPiUNCiAgZmlsdGVyKA0KICAgIGNhdWdodF9ieSAlaW4lIHRoZV9nYW5nDQogICkNCmBgYA0KDQpgYGB7cn0NCiMgdGFibGUgb2YgbW9zdCBjYXRjaGVzDQpjb3VudChjYXVnaHRfZGYsIGNhdWdodF9ieSkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCmBgYA0KDQpgYGB7cn0NCiMgcGxvdCB0aGUgcmVsYXRpb25zaGlwIG92ZXIgdGltZQ0KY2F1Z2h0X2RmMiA8LSBjYXVnaHRfZGYgJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICByZWFsID0gaWZlbHNlKG1vbnN0ZXJfcmVhbCA9PSAiVFJVRSIsICJSZWFsIiwgIkZha2UiKSwNCiAgICBtb25zdGVyX2RldGFpbHMgPSBwYXN0ZTAobW9uc3Rlcl9uYW1lLCAiICgiLCByZWFsLCAiIE1vbnN0ZXIocykpIGluICciLCB0aXRsZSwgIicgb24gIiwgZGF0ZV9haXJlZCwgIi4iKQ0KICApICU+JQ0KICBncm91cF9ieSh5ZWFyLCBjYXVnaHRfYnkpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIG4gPSBuKCksDQogICAgY2F1Z2h0X25hbWVzID0gcGFzdGUobW9uc3Rlcl9kZXRhaWxzLCBjb2xsYXBzZSA9ICJcbiIpDQogICkNCg0KcGxvdF9tb25zdGVyc19jYXVnaHQgPC0gZnVuY3Rpb24oZ2FuZ19tZW1iZXIsIGRmID0gY2F1Z2h0X2RmMikgew0KICBmaWcgPC0gZGYgJT4lDQogICAgZmlsdGVyKGNhdWdodF9ieSA9PSBnYW5nX21lbWJlcikgJT4lDQogICAgcGxvdF9seSgNCiAgICAgIHR5cGUgPSAnYmFyJywNCiAgICAgIHggPSB+eWVhciwNCiAgICAgIHkgPSB+biwNCiAgICAgIHRleHQgPSB+cGFzdGUwKCI8Yj5Nb25zdGVycyBDYXVnaHQ8L2I+PGJyPjxicj4iLA0KICAgICAgICAgICAgICAgICAgICAgY2F1Z2h0X25hbWVzKSwNCiAgICAgIHNob3dsZWdlbmQgPSBGQUxTRQ0KICAgICkgJT4lDQogICAgbGF5b3V0KA0KICAgICAgeGF4aXMgPSBsaXN0KA0KICAgICAgICBzaG93bGluZSA9IFRSVUUsDQogICAgICAgIHNob3d0aWNrbGFiZWxzID0gRkFMU0UsDQogICAgICAgIG1pcnJvciA9ICJ0aWNrcyIsDQogICAgICAgIGxpbmVjb2xvciA9IHRvUkdCKCJibGFjayIpLA0KICAgICAgICBsaW5ld2lkdGggPSAyLA0KICAgICAgICByYW5nZSA9IGMobWluKGNhdWdodF9kZjIkeWVhciksIG1heChjYXVnaHRfZGYyJHllYXIpKQ0KICAgICAgKSwNCiAgICAgIHlheGlzID0gbGlzdCgNCiAgICAgICAgdGl0bGUgPSB+cGFzdGUwKCI8Yj4iLCBnYW5nX21lbWJlciwgIjwvYj4iKSwNCiAgICAgICAgc2hvd2xpbmUgPSBUUlVFLA0KICAgICAgICBtaXJyb3IgPSAidGlja3MiLA0KICAgICAgICBsaW5lY29sb3IgPSB0b1JHQigiYmxhY2siKSwNCiAgICAgICAgbGluZXdpZHRoID0gMiwNCiAgICAgICAgcmFuZ2UgPSBjKG1pbihjYXVnaHRfZGYyJG4pLCBtYXgoY2F1Z2h0X2RmMiRuKSArIDEpDQogICAgICApDQogICAgKQ0KICANCiAgZmlnDQp9DQoNCnN1YnBsb3QoDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJGcmVkIiksDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJEYXBobmllIiksDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJWZWxtYSIpLA0KICBwbG90X21vbnN0ZXJzX2NhdWdodCgiU2hhZ2d5IiksDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJTY29vYnkiKSwNCiAgbnJvd3MgPSBsZW5ndGgodGhlX2dhbmcpLA0KICBzaGFyZVggPSBGQUxTRSwNCiAgc2hhcmVZID0gRkFMU0UsDQogIHRpdGxlWCA9IEZBTFNFLA0KICB0aXRsZVkgPSBUUlVFDQopICU+JQ0KICBsYXlvdXQoDQogICAgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywNCiAgICB0aXRsZSA9ICc8Yj48aT5Nb25zdGVycyBDYXB0dXJlZCBBbm51YWx5IEJ5Li4uPC9pPjwvYj4nLA0KICAgIHdpZHRoID0gOTAwLA0KICAgIGhlaWdodCA9IDYwMCwNCiAgICBsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gJ2gnKQ0KICApDQpgYGANCg0KDQojIyBXaG8gd2FzIGNhcHR1cmVkIGJ5IG1vbnN0ZXJzIHRoZSBtb3N0Pw0KDQpgYGB7cn0NCiMgdGlkeSBkYXRhIGZyYW1lIGZvciBjYXB0dXJlZCBieSB0aGUgbW9uc3Rlcg0KY2FwdHVyZWRfZGYgPC0gbW9uc3RlcnMNCmNhcHR1cmVkX2RmJGNhcHR1cmVkIDwtIGFwcGx5KGNhcHR1cmVkX2RmICU+JSBzZWxlY3Qoc3RhcnRzX3dpdGgoImNhcHR1cmVkXyIpKSwgMSwgZnVuY3Rpb24oeCkgcGFzdGUoeFt4ICE9ICJGQUxTRSIgJiB4ICE9ICJOVUxMIl0sIGNvbGxhcHNlID0gIiwiKSkNCiNjYXB0dXJlZCRjYXB0dXJlZF9ieVtpcy5uYShjYXB0dXJlZCRjYXVnaHRfYnkpXSA8LSAiTm90IENhdWdodCINCmNhcHR1cmVkX2RmIDwtIGNhcHR1cmVkX2RmICU+JQ0KICBzZXBhcmF0ZV9yb3dzKGNhcHR1cmVkLCBzZXAgPSAiLCIpICU+JQ0KICBmaWx0ZXIoDQogICAgY2FwdHVyZWQgJWluJSB0aGVfZ2FuZw0KICApDQpgYGANCg0KYGBge3J9DQpjb3VudChjYXB0dXJlZF9kZiwgY2FwdHVyZWQpICU+JQ0KICBhcnJhbmdlKGRlc2MobikpDQpgYGANCg0KYGBge3J9DQojIHBsb3QgdGhlIHJlbGF0aW9uc2hpcA0KY2FwdHVyZWRfZGYyIDwtIGNhcHR1cmVkX2RmICU+JQ0KICBtdXRhdGUoDQogICAgeWVhciA9IHllYXIoZGF0ZV9haXJlZCksDQogICAgcmVhbCA9IGlmZWxzZShtb25zdGVyX3JlYWwgPT0gIlRSVUUiLCAiUmVhbCIsICJGYWtlIiksDQogICAgbW9uc3Rlcl9kZXRhaWxzID0gcGFzdGUwKG1vbnN0ZXJfbmFtZSwgIiAoIiwgcmVhbCwgIiBNb25zdGVyKHMpKSBpbiAnIiwgdGl0bGUsICInIG9uICIsIGRhdGVfYWlyZWQsICIuIikNCiAgKSAlPiUNCiAgZ3JvdXBfYnkoeWVhciwgY2FwdHVyZWQpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIG4gPSBuKCksDQogICAgY2FwdHVyZWRfZGV0YWlscyA9IHBhc3RlKG1vbnN0ZXJfZGV0YWlscywgY29sbGFwc2UgPSAiXG4iKQ0KICApDQoNCnBsb3RfbW9uc3RlcnNfY2FwdHVyZXMgPC0gZnVuY3Rpb24oZ2FuZ19tZW1iZXIsIGRmID0gY2FwdHVyZWRfZGYyKSB7DQogIGZpZyA8LSBkZiAlPiUNCiAgICBmaWx0ZXIoY2FwdHVyZWQgPT0gZ2FuZ19tZW1iZXIpICU+JQ0KICAgIHBsb3RfbHkoDQogICAgICB0eXBlID0gJ2JhcicsDQogICAgICB4ID0gfnllYXIsDQogICAgICB5ID0gfm4sDQogICAgICB0ZXh0ID0gfnBhc3RlMCgiPGI+Q2FwdHVyZWQgQnkuLi48L2I+PGJyPjxicj4iLA0KICAgICAgICAgICAgICAgICAgICAgY2FwdHVyZWRfZGV0YWlscyksDQogICAgICBzaG93bGVnZW5kID0gRkFMU0UNCiAgICApICU+JQ0KICAgIGxheW91dCgNCiAgICAgIHhheGlzID0gbGlzdCgNCiAgICAgICAgc2hvd2xpbmUgPSBUUlVFLA0KICAgICAgICBzaG93dGlja2xhYmVscyA9IEZBTFNFLA0KICAgICAgICBtaXJyb3IgPSAidGlja3MiLA0KICAgICAgICBsaW5lY29sb3IgPSB0b1JHQigiYmxhY2siKSwNCiAgICAgICAgbGluZXdpZHRoID0gMiwNCiAgICAgICAgcmFuZ2UgPSBjKG1pbihjYXB0dXJlZF9kZjIkeWVhciksIG1heChjYXB0dXJlZF9kZjIkeWVhcikpDQogICAgICApLA0KICAgICAgeWF4aXMgPSBsaXN0KA0KICAgICAgICB0aXRsZSA9IH5wYXN0ZTAoIjxiPiIsIGdhbmdfbWVtYmVyLCAiPC9iPiIpLA0KICAgICAgICBzaG93bGluZSA9IFRSVUUsDQogICAgICAgIG1pcnJvciA9ICJ0aWNrcyIsDQogICAgICAgIGxpbmVjb2xvciA9IHRvUkdCKCJibGFjayIpLA0KICAgICAgICBsaW5ld2lkdGggPSAyLA0KICAgICAgICByYW5nZSA9IGMobWluKGNhcHR1cmVkX2RmMiRuKSwgbWF4KGNhcHR1cmVkX2RmMiRuKSArIDEpDQogICAgICApDQogICAgKQ0KICANCiAgZmlnDQp9DQoNCnN1YnBsb3QoDQogIHBsb3RfbW9uc3RlcnNfY2FwdHVyZXMoIkZyZWQiKSwNCiAgcGxvdF9tb25zdGVyc19jYXB0dXJlcygiRGFwaG5pZSIpLA0KICBwbG90X21vbnN0ZXJzX2NhcHR1cmVzKCJWZWxtYSIpLA0KICBwbG90X21vbnN0ZXJzX2NhcHR1cmVzKCJTaGFnZ3kiKSwNCiAgcGxvdF9tb25zdGVyc19jYXB0dXJlcygiU2Nvb2J5IiksDQogIG5yb3dzID0gbGVuZ3RoKHRoZV9nYW5nKSwNCiAgc2hhcmVYID0gRkFMU0UsDQogIHNoYXJlWSA9IEZBTFNFLA0KICB0aXRsZVggPSBGQUxTRSwNCiAgdGl0bGVZID0gVFJVRQ0KKSAlPiUNCiAgbGF5b3V0KA0KICAgIG1vZGUgPSAnbGluZXMrbWFya2VycycsDQogICAgdGl0bGUgPSAnPGI+PGk+Smlua2llcyEgLi4uIGhhcyBiZWVuIGNhcHR1cmVkIGJ5IHRoZSBtb25zdGVyITwvaT48L2I+JywNCiAgICB3aWR0aCA9IDkwMCwNCiAgICBoZWlnaHQgPSA2MDAsDQogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICdoJykNCiAgKQ0KYGBgDQoNCiMjIFdobyB1bm1hc2tlZCB0aGUgbW9zdCBtb25zdGVycz8NCg0KYGBge3J9DQojIHRpZHkgZGF0YSBmcmFtZSBmb3IgY2FwdHVyZWQgYnkgdGhlIG1vbnN0ZXINCnVubWFza2VkX2RmIDwtIG1vbnN0ZXJzDQp1bm1hc2tlZF9kZiR1bm1hc2tlZF9ieSA8LSBhcHBseSh1bm1hc2tlZF9kZiAlPiUgc2VsZWN0KHN0YXJ0c193aXRoKCJ1bm1hc2tfIikpLCAxLCBmdW5jdGlvbih4KSBwYXN0ZSh4W3ggIT0gIkZBTFNFIiAmIHggIT0gIk5VTEwiXSwgY29sbGFwc2UgPSAiLCIpKQ0KdW5tYXNrZWRfZGYkdW5tYXNrZWRfYnlbaXMubmEodW5tYXNrZWRfZGYkdW5tYXNrZWRfYnkpXSA8LSAiTm90IFVubWFza2VkIg0KdW5tYXNrZWRfZGYgPC0gdW5tYXNrZWRfZGYgJT4lDQogIHNlcGFyYXRlX3Jvd3ModW5tYXNrZWRfYnksIHNlcCA9ICIsIikgJT4lDQogIGZpbHRlcigNCiAgICB1bm1hc2tlZF9ieSAlaW4lIHRoZV9nYW5nDQogICkNCmBgYA0KDQpgYGB7cn0NCmNvdW50KHVubWFza2VkX2RmLCB1bm1hc2tlZF9ieSkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCmBgYA0KDQpgYGB7cn0NCiMgcGxvdCB0aGUgcmVsYXRpb25zaGlwDQp1bm1hc2tlZF9kZjIgPC0gdW5tYXNrZWRfZGYgJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICByZWFsID0gaWZlbHNlKG1vbnN0ZXJfcmVhbCA9PSAiVFJVRSIsICJSZWFsIiwgIkZha2UiKSwNCiAgICBtb25zdGVyX2RldGFpbHMgPSBwYXN0ZTAobW9uc3Rlcl9uYW1lLCAiICgiLCByZWFsLCAiIE1vbnN0ZXIocykpIGluICciLCB0aXRsZSwgIicgb24gIiwgZGF0ZV9haXJlZCwgIi4iKQ0KICApICU+JQ0KICBncm91cF9ieSh5ZWFyLCB1bm1hc2tlZF9ieSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgbiA9IG4oKSwNCiAgICB1bm1hc2tfZGV0YWlscyA9IHBhc3RlKG1vbnN0ZXJfZGV0YWlscywgY29sbGFwc2UgPSAiXG4iKQ0KICApDQoNCnBsb3RfbW9uc3RlcnNfdW5tYXNrcyA8LSBmdW5jdGlvbihnYW5nX21lbWJlciwgZGYgPSB1bm1hc2tlZF9kZjIpIHsNCiAgZmlnIDwtIGRmICU+JQ0KICAgIGZpbHRlcih1bm1hc2tlZF9ieSA9PSBnYW5nX21lbWJlcikgJT4lDQogICAgcGxvdF9seSgNCiAgICAgIHR5cGUgPSAnYmFyJywNCiAgICAgIHggPSB+eWVhciwNCiAgICAgIHkgPSB+biwNCiAgICAgIHRleHQgPSB+cGFzdGUwKCI8Yj5Vbm1hc2tlZCBCeS4uLjwvYj48YnI+PGJyPiIsDQogICAgICAgICAgICAgICAgICAgICB1bm1hc2tfZGV0YWlscyksDQogICAgICBzaG93bGVnZW5kID0gRkFMU0UNCiAgICApICU+JQ0KICAgIGxheW91dCgNCiAgICAgIHhheGlzID0gbGlzdCgNCiAgICAgICAgc2hvd2xpbmUgPSBUUlVFLA0KICAgICAgICBzaG93dGlja2xhYmVscyA9IEZBTFNFLA0KICAgICAgICBtaXJyb3IgPSAidGlja3MiLA0KICAgICAgICBsaW5lY29sb3IgPSB0b1JHQigiYmxhY2siKSwNCiAgICAgICAgbGluZXdpZHRoID0gMiwNCiAgICAgICAgcmFuZ2UgPSBjKG1pbih1bm1hc2tlZF9kZjIkeWVhciksIG1heCh1bm1hc2tlZF9kZjIkeWVhcikpDQogICAgICApLA0KICAgICAgeWF4aXMgPSBsaXN0KA0KICAgICAgICB0aXRsZSA9IH5wYXN0ZTAoIjxiPiIsIGdhbmdfbWVtYmVyLCAiPC9iPiIpLA0KICAgICAgICBzaG93bGluZSA9IFRSVUUsDQogICAgICAgIG1pcnJvciA9ICJ0aWNrcyIsDQogICAgICAgIGxpbmVjb2xvciA9IHRvUkdCKCJibGFjayIpLA0KICAgICAgICBsaW5ld2lkdGggPSAyLA0KICAgICAgICByYW5nZSA9IGMobWluKHVubWFza2VkX2RmMiRuKSwgbWF4KHVubWFza2VkX2RmMiRuKSArIDEpDQogICAgICApDQogICAgKQ0KICANCiAgZmlnDQp9DQoNCnN1YnBsb3QoDQogIHBsb3RfbW9uc3RlcnNfdW5tYXNrcygiRnJlZCIpLA0KICBwbG90X21vbnN0ZXJzX3VubWFza3MoIkRhcGhuaWUiKSwNCiAgcGxvdF9tb25zdGVyc191bm1hc2tzKCJWZWxtYSIpLA0KICBwbG90X21vbnN0ZXJzX3VubWFza3MoIlNoYWdneSIpLA0KICBwbG90X21vbnN0ZXJzX3VubWFza3MoIlNjb29ieSIpLA0KICBucm93cyA9IGxlbmd0aCh0aGVfZ2FuZyksDQogIHNoYXJlWCA9IEZBTFNFLA0KICBzaGFyZVkgPSBGQUxTRSwNCiAgdGl0bGVYID0gRkFMU0UsDQogIHRpdGxlWSA9IFRSVUUNCikgJT4lDQogIGxheW91dCgNCiAgICBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLA0KICAgIHRpdGxlID0gJzxiPjxpPk1vbnN0ZXJzIFVubWFza2VkIEFubnVhbHkgQnkuLi48L2k+PC9iPicsDQogICAgd2lkdGggPSA5MDAsDQogICAgaGVpZ2h0ID0gNjAwLA0KICAgIGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAnaCcpDQogICkNCmBgYA0KDQojIyBXaGF0IG1vbnN0ZXIgdHlwZXMgYXJlIG1vc3QgbGlrZWx5IHRvIGdldCBhd2F5Pw0KDQpgYGB7cn0NCm5fbm90X2NhdWdodCA8LSBucm93KGZpbHRlcihzY29vYnlkb28sIGNhdWdodF9ub3QgPT0gIlRSVUUiKSkNCmdvdF9hd2F5X3JhdGUgPC0gcGFzdGUwKHJvdW5kKDEwMCAqIChuX25vdF9jYXVnaHQgLyBucm93KHNjb29ieWRvbykpLCAyKSwgIiUiKQ0KYGBgDQoNCkZpcnN0IG9mIGFsbCwgdGhlIG1vbnN0ZXIocykgZ290IGF3YXkgYXQgdGhlIGVuZCBvZiBvbmx5IGByIG5fbm90X2NhdWdodGAgb3V0IG9mIGByIG5yb3coc2Nvb2J5ZG9vKWAgZXBpc29kZXMgKHJhdGUgb2YgYHIgZ290X2F3YXlfcmF0ZWApLiBPZiB0aG9zZSBgciBuX25vdF9jYXVnaHRgLCB0aGUgZm9sbG93aW5nIGJyZWFrcyBkb3duIHRoZSBzdWNjZXNzIHJhdGUgYnkgbW9uc3RlciB0eXBlLg0KDQpgYGB7cn0NCmVzY2FwZV9zdGF0cyA8LSBzY29vYnlkb28gJT4lDQogIGZpbHRlcihjYXVnaHRfbm90ID09ICJUUlVFIikgJT4lDQogICMgbG9va3MgbGlrZSB0aGVyZSB3ZXJlIGEgZmV3IG1pc3BlbGxpbmdzDQogIG11dGF0ZShtb25zdGVyX3R5cGUgPSBpZmVsc2Uoc3RyX2RldGVjdChtb25zdGVyX3R5cGUsICIoRGlzZ3Vpc2VkfERpc2d1aXNlZHxEaXN1Z2lzZWQpIiksICJEaXNndWlzZWQiLCBtb25zdGVyX3R5cGUpKSAlPiUNCiAgc2VwYXJhdGVfcm93cyhtb25zdGVyX3R5cGUsIHNlcCA9ICIsIikgJT4lDQogIGdyb3VwX2J5KG1vbnN0ZXJfdHlwZSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgbl9lc2NhcGVkID0gbigpLA0KICAgIG1lYW5faW1kYl9lc2NhcGVkID0gbWVhbihhcy5kb3VibGUoaW1kYikpDQogICkNCg0KaW1kYl9lZmZlY3RfY2F0ZWdvcmllcyA8LSBjKA0KICAiQmV0dGVyIFRoYW4gQXZlcmFnZSBFcGlzb2RlIiwNCiAgIlNsaWdodGx5IEJldHRlciBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICJTbGlnaHRseSBXb3JzZSBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICJXb3JzZSBUaGFuIEF2ZXJhZ2UgRXBpc29kZSINCikNCg0KZXNjYXBlX3N0YXRzMiA8LSBzY29vYnlkb28gJT4lDQogIG11dGF0ZShtb25zdGVyX3R5cGUgPSBpZmVsc2Uoc3RyX2RldGVjdChtb25zdGVyX3R5cGUsICIoRGlzZ3Vpc2VkfERpc2d1aXNlZHxEaXN1Z2lzZWQpIiksICJEaXNndWlzZWQiLCBtb25zdGVyX3R5cGUpKSAlPiUNCiAgc2VwYXJhdGVfcm93cyhtb25zdGVyX3R5cGUsIHNlcCA9ICIsIikgJT4lDQogIGZpbHRlcihpbWRiICE9ICJOVUxMIikgJT4lDQogIGdyb3VwX2J5KG1vbnN0ZXJfdHlwZSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgbl90b3RhbCA9IG4oKSwNCiAgICBtZWFuX2ltZGIgPSBtZWFuKGFzLmRvdWJsZShpbWRiKSkNCiAgKSAlPiUNCiAgaW5uZXJfam9pbihlc2NhcGVfc3RhdHMpICU+JQ0KICBtdXRhdGUoDQogICAgZXNjYXBlX3JhdGUgPSByb3VuZCgxMDAgKiAobl9lc2NhcGVkIC8gbl90b3RhbCkpLA0KICAgIGVzY2FwZV9pbWRiX2VmZmVjdCA9IG1lYW5faW1kYl9lc2NhcGVkIC0gbWVhbl9pbWRiLA0KICAgIGltZGJfZWZmZWN0X2NhdGVnb3J5ID0gY2FzZV93aGVuKA0KICAgICAgZXNjYXBlX2ltZGJfZWZmZWN0IDwgLTEgfiAiV29yc2UgVGhhbiBBdmVyYWdlIEVwaXNvZGUiLA0KICAgICAgZXNjYXBlX2ltZGJfZWZmZWN0IDwgMCB+ICJTbGlnaHRseSBXb3JzZSBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICAgICBlc2NhcGVfaW1kYl9lZmZlY3QgPCAxIH4gIlNsaWdodGx5IEJldHRlciBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICAgICBlc2NhcGVfaW1kYl9lZmZlY3QgPj0gMSB+ICJCZXR0ZXIgVGhhbiBBdmVyYWdlIEVwaXNvZGUiDQogICAgKSwNCiAgICBpbWRiX2VmZmVjdF9jYXRlZ29yeSA9IGZhY3RvcihpbWRiX2VmZmVjdF9jYXRlZ29yeSwgbGV2ZWxzID0gaW1kYl9lZmZlY3RfY2F0ZWdvcmllcykNCiAgKQ0KDQplc2NhcGVfc3RhdHMyICU+JQ0KICBwbG90X2x5KA0KICAgIHR5cGUgPSAnYmFyJywNCiAgICB4ID0gfmZjdF9yZW9yZGVyKG1vbnN0ZXJfdHlwZSwgZXNjYXBlX3JhdGUsIC5kZXNjID0gVFJVRSksDQogICAgeSA9IH5lc2NhcGVfcmF0ZSwNCiAgICBjb2xvciA9IH5pbWRiX2VmZmVjdF9jYXRlZ29yeSwNCiAgICBjb2xvcnMgPSBjb2xvcl9zY2hlbWUyLA0KICAgIHRleHQgPSB+cGFzdGUwKCI8Yj4iLCBtb25zdGVyX3R5cGUsICI8L2I+PGJyPjxicj4iLCANCiAgICAgICAgICAgICAgICAgICAiRXNjYXBlZCAiLCBuX2VzY2FwZWQsICIgb3V0IG9mICIsIG5fdG90YWwsICIgZXBpc29kZXMuPGJyPiIsDQogICAgICAgICAgICAgICAgICAgIkF2ZXJhZ2UgRXBpc29kZSBJTURiIFNjb3JlIChBbGwpOiAiLCByb3VuZChtZWFuX2ltZGIsIDEpLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgIkF2ZXJhZ2UgRXBpc29kZSBJTURiIFNjb3JlIChFc2NhcGVkKTogIiwgcm91bmQobWVhbl9pbWRiX2VzY2FwZWQsIDEpKQ0KICApICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAnSG93IG9mdGVuIGRvIFNjb29ieSBEb28gbW9uc3RlcnMgZ28gdW5jYXVnaHQsIGFuZCBob3cgZG9lcyB0aGF0IGFmZmVjdCB0aGUgZXBpc29kZT8nLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJNb25zdGVyIFR5cGUiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRXNjYXBlIFJhdGUgKCUpIiksDQogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICdoJywgeSA9IC0wLjMpLA0KICAgIHdpZHRoID0gOTAwLA0KICAgIGhlaWdodCA9IDUwMA0KICApDQpgYGANCg==